package org.rectask.model;

import java.util.ArrayList;
import java.util.Iterator;

/**
 * A sorted list of dates. 
 * 
 * @author sergut
 *
 */
public class SortedDateList {
    /**
     *  The list of date, always empty or sorted, with no duplicates.
     *  
     *   Earlier dates are before, later dates are at the end.
     */
    ArrayList<Date> dates;
    
    /**
     * A new SortedDateList.
     */
    public SortedDateList() {
	dates = new ArrayList<Date>();
    }

    /**
     * Adds a date to the list, keeping the list sorted. If the date
     * is already in the list, an IllegalArgumentException is thrown.
     * 
     *  @param date the date to be inserted
     *  
     *  @return true if this collection changed as a result of the call, false otherwise
     *  
     */
    public boolean add(Date date) {
	if (dates.contains(date)) 
	    return false; 
	
	boolean added = false;
	for (int i=0; i < dates.size() && !added; i++) {
	    Date nextDate = dates.get(i);
	    if (date.before(nextDate)) {
		dates.add(i, date);
		added = true;
	    }
	}
	if (!added) {
	    dates.add(date);
	}
	return true;
    }
    
    /**
     * Removes a date from the list, keeping the list sorted. 
     *  
     *  @param date the date to be removed
     *  
     *  @return true if this collection changed as a result of the call
     *  
     */
    public boolean remove(Date date) {
	return dates.remove(date);
    }

    /**
     * Adds another date to the list of execution dates, if 
     * it is not there. If it is, it removes it. 
     */
    public void toggle(Date date) {
	if (dates.contains(date)) 
	    dates.remove(date);
	else
	    dates.add(date);	    
    }
    
    /**
     * Return true if this list contains the date, false otherwise. 
     */
    public boolean contains(Date date) {
	return dates.contains(date);
    }
    
    /**
     * Returns the list of dates
     */
    public ArrayList<Date> getDates() {
	return dates;
    }
    
    /**
     * Returns the last date in this list that happens before
     * or at the given date. 
     */
    // TODO: do a O(log n) search, instead of a O(n) search
    public Date getLastDateBefore(Date date) {
	Date result = null;
	boolean finished = false;
	for (Iterator<Date> itr = dates.iterator(); itr.hasNext() && !finished;) {
	    Date lastDate = itr.next();
	    if (lastDate.before(date))
		result = lastDate;
	    else if (lastDate.equals(date))
		result = lastDate;
	    else
		finished = true;
	}
	return result;
    }

    /**
     * Returns the last date in this list that happens before
     * (not at) the given date. 
     * 
     */
    // TODO: do a O(log n) search, instead of a O(n) search
    public Date getLastDateStrictlyBefore(Date date) {
	Date result = null;
	boolean finished = false;
	for (Iterator<Date> itr = dates.iterator(); itr.hasNext() && !finished;) {
	    Date lastDate = itr.next();
	    if (lastDate.before(date))
		result = lastDate;
	    else
		finished = true;
	}
	return result;
    }

    /**
     * Returns the first date of this list that happens after
     * or on the given date. 
     */
    // TODO: do a O(log n) search, instead of a O(n) search
    public Date getFirstDateAfter(Date date) {
	for (Iterator<Date> itr = dates.iterator(); itr.hasNext();) {
	    Date nextDate = itr.next();
	    if (nextDate.equals(date))
		return nextDate;
	    else if (nextDate.after(date))
		return nextDate;
	}
	return null;
    }

    /**
     * Returns the first date of this list that happens after
     * (not on) the given date. 
     * 
     */
    // TODO: do a O(log n) search, instead of a O(n) search
    public Date getFirstDateStrictlyAfter(Date date) {
	for (Iterator<Date> itr = dates.iterator(); itr.hasNext();) {
	    Date nextDate = itr.next();
	    if (nextDate.after(date))
		return nextDate;
	}
	return null;
    }
    
    /**
     * Returns an iterators over this list. 
     */
    public Iterator<Date> iterator() {
	return this.dates.iterator();
    }
}
